/*
 * Copyright (c) 1998-2011 Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.hijor.mail;

import java.io.*;
import java.util.*;
import java.text.*;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.*;
import javax.mail.Part;
import javax.mail.internet.*;
import javax.activation.*;

/**
 * This is a servlet that demonstrates the use of JavaMail APIs in a 3-tier
 * application. It allows the user to login to an IMAP store, list all the
 * messages in the INBOX folder, view selected messages, compose and send a
 * message, and logout. <p> Please note: This is NOT an example of how to write
 * servlets! This is simply to show that JavaMail can be used in a servlet. <p>
 * For more information on this servlet, see the JavaMailServlet.README.txt
 * file. <p> For more information on servlets, see <a
 * href="http://java.sun.com/products/java-server/servlets/index.html">
 * http://java.sun.com/products/java-server/servlets/index.html</a>
 *
 * @author Max Spivak
 */
public class JavaMailServlet extends HttpServlet implements SingleThreadModel {

    String protocol = "imap";
    String mbox = "INBOX";
    final int IMAPPort = 143;

    /**
     * This method handles the "POST" submission from two forms: the login form
     * and the message compose form. The login form has the following
     * parameters:
     * <code>hostname</code>,
     * <code>username</code>, and
     * <code>password</code>. The
     * <code>send</code> parameter denotes that the method is processing the
     * compose form submission.
     */
    public void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {

        // get the session
        HttpSession ssn = req.getSession(true);

        String send = req.getParameter("send");
        String host = req.getParameter("hostname");
        String user = req.getParameter("username");
        String passwd = req.getParameter("password");


        ServletOutputStream out = res.getOutputStream();
        res.setContentType("text/html");
        out.println("<html><body bgcolor=\"#CCCCFF\">");

        if (send != null) {
            // process message sending
            send(req, res, out, ssn);
            
        } else {
            // initial login
            URLName url = new URLName(protocol, host, IMAPPort, mbox, user, passwd);
            // create 
            MailUserData mud = new MailUserData(url);
            ssn.putValue("javamailservlet", mud);

            try {
                Properties props = System.getProperties();
                props.put("mail.smtp.host", host);
                Session session = Session.getDefaultInstance(props, null);
                session.setDebug(false);
                Store store = session.getStore(url);
                store.connect();
                Folder folder = store.getDefaultFolder();
                if (folder == null) {
                    throw new MessagingException("No default folder");
                }

                folder = folder.getFolder(mbox);
                if (folder == null) {
                    throw new MessagingException("Invalid folder");
                }

                folder.open(Folder.READ_WRITE);
                int totalMessages = folder.getMessageCount();
                Message[] msgs = folder.getMessages();
                FetchProfile fp = new FetchProfile();
                fp.add(FetchProfile.Item.ENVELOPE);
                folder.fetch(msgs, fp);

                // track who logged in
                System.out.println("Login from: " + store.getURLName());

                // save stuff into MUD
                mud.setSession(session);
                mud.setStore(store);
                mud.setFolder(folder);

                // splash
                out.print("<center>");
                out.print("<font face=\"Arial,Helvetica\" font size=+3>");
                out.println("<b>Welcome to JavaMail!</b></font></center><p>");

                // folder table
                out.println("<table width=\"50%\" border=0 align=center>");
                // folder name column header
                out.print("<tr><td width=\"75%\" bgcolor=\"#ffffcc\">");
                out.print("<font face=\"Arial,Helvetica\" font size=-1>");
                out.println("<b>FolderName</b></font></td><br>");
                // msg count column header
                out.print("<td width=\"25%\" bgcolor=\"#ffffcc\">");
                out.print("<font face=\"Arial,Helvetica\" font size=-1>");
                out.println("<b>Messages</b></font></td><br>");
                out.println("</tr>");
                // folder name
                out.print("<tr><td width=\"75%\" bgcolor=\"#ffffff\">");
                out.print("<a href=\"" + HttpUtils.getRequestURL(req) + "\">"
                        + "Inbox" + "</a></td><br>");
                // msg count
                out.println("<td width=\"25%\" bgcolor=\"#ffffff\">"
                        + totalMessages + "</td>");
                out.println("</tr>");
                out.println("</table");
            } catch (Exception ex) {
                out.println(ex.toString());
            } finally {
                out.println("</body></html>");
                out.close();
            }
        }
    }

    /**
     * This method handles the GET requests for the client.
     */
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {

        HttpSession ses = req.getSession(false); // before we write to out
        ServletOutputStream out = res.getOutputStream();
        MailUserData mud = getMUD(ses);

        if (mud == null) {
            res.setContentType("text/html");
            out.println("<html><body>Please Login (no session)</body></html>");
            out.close();
            return;
        }

        if (!mud.getStore().isConnected()) {
            res.setContentType("text/html");
            out.println("<html><body>Not Connected To Store</body></html>");
            out.close();
            return;
        }


        // mux that takes a GET request, based on parameters figures
        // out what it should do, and routes it to the
        // appropriate method

        // get url parameters
        String msgStr = req.getParameter("message");
        String logout = req.getParameter("logout");
        String compose = req.getParameter("compose");
        String part = req.getParameter("part");
        int msgNum = -1;
        int partNum = -1;

        // process url params
        if (msgStr != null) {
            // operate on message "msgStr"
            msgNum = Integer.parseInt(msgStr);

            if (part == null) {
                // display message "msgStr"
                res.setContentType("text/html");
                displayMessage(mud, req, out, msgNum);

            } else {
                // display part "part" in message "msgStr"
                partNum = Integer.parseInt(part);
                displayPart(mud, msgNum, partNum, out, res);
            }

        } else if (compose != null) {
            // display compose form
            compose(mud, res, out);

        } else if (logout != null) {
            // process logout
            try {
                mud.getFolder().close(false);
                mud.getStore().close();
                ses.invalidate();
                out.println("<html><body>Logged out OK</body></html>");
            } catch (MessagingException mex) {
                out.println(mex.toString());
            }

        } else {
            // display headers
            displayHeaders(mud, req, out);
        }
    }

    /* main method to display messages */
    private void displayMessage(MailUserData mud, HttpServletRequest req,
            ServletOutputStream out, int msgNum)
            throws IOException {

        out.println("<html>");
        out.println("<HEAD><TITLE>JavaMail Servlet</TITLE></HEAD>");
        out.println("<BODY bgcolor=\"#ccccff\">");
        out.print("<center><font face=\"Arial,Helvetica\" ");
        out.println("font size=\"+3\"><b>");
        out.println("Message " + (msgNum + 1) + " in folder "
                + mud.getStore().getURLName()
                + "/INBOX</b></font></center><p>");

        try {
            Message msg = mud.getFolder().getMessage(msgNum);

            // first, display this message's headers
            displayMessageHeaders(mud, msg, out);

            // and now, handle the content
            Object o = msg.getContent();

            //if (o instanceof String) {
            if (msg.isMimeType("text/plain")) {
                out.println("<pre>");
                out.println((String) o);
                out.println("</pre>");
                //} else if (o instanceof Multipart){
            } else if (msg.isMimeType("multipart/*")) {
                Multipart mp = (Multipart) o;
                int cnt = mp.getCount();
                for (int i = 0; i < cnt; i++) {
                    displayPart(mud, msgNum, mp.getBodyPart(i), i, req, out);
                }
            } else {
                out.println(msg.getContentType());
            }

        } catch (MessagingException mex) {
            out.println(mex.toString());
        }

        out.println("</BODY></html>");
        out.close();
    }

    /**
     * This method displays a message part.
     * <code>text/plain</code> content parts are displayed inline. For all other
     * parts, a URL is generated and displayed; clicking on the URL brings up
     * the part in a separate page.
     */
    private void displayPart(MailUserData mud, int msgNum, Part part,
            int partNum, HttpServletRequest req,
            ServletOutputStream out)
            throws IOException {

        if (partNum != 0) {
            out.println("<p><hr>");
        }

        try {

            String sct = part.getContentType();
            if (sct == null) {
                out.println("invalid part");
                return;
            }
            ContentType ct = new ContentType(sct);

            if (partNum != 0) {
                out.println("<b>Attachment Type:</b> "
                        + ct.getBaseType() + "<br>");
            }

            if (ct.match("text/plain")) {
                // display text/plain inline
                out.println("<pre>");
                out.println((String) part.getContent());
                out.println("</pre>");

            } else {
                // generate a url for this part
                String s;
                if ((s = part.getFileName()) != null) {
                    out.println("<b>Filename:</b> " + s + "<br>");
                }
                s = null;
                if ((s = part.getDescription()) != null) {
                    out.println("<b>Description:</b> " + s + "<br>");
                }

                out.println("<a href=\""
                        + HttpUtils.getRequestURL(req)
                        + "?message="
                        + msgNum + "&part="
                        + partNum + "\">Display Attachment</a>");
            }
        } catch (MessagingException mex) {
            out.println(mex.toString());
        }
    }

    /**
     * This method gets the stream from for a given msg part and pushes it out
     * to the browser with the correct content type. Used to display attachments
     * and relies on the browser's content handling capabilities.
     */
    private void displayPart(MailUserData mud, int msgNum,
            int partNum, ServletOutputStream out,
            HttpServletResponse res)
            throws IOException {

        Part part = null;

        try {
            Message msg = mud.getFolder().getMessage(msgNum);

            Multipart mp = (Multipart) msg.getContent();
            part = mp.getBodyPart(partNum);

            String sct = part.getContentType();
            if (sct == null) {
                out.println("invalid part");
                return;
            }
            ContentType ct = new ContentType(sct);

            res.setContentType(ct.getBaseType());
            InputStream is = part.getInputStream();
            int i;
            while ((i = is.read()) != -1) {
                out.write(i);
            }
            out.flush();
            out.close();
        } catch (MessagingException mex) {
            out.println(mex.toString());
        }
    }

    /**
     * This is a utility message that pretty-prints the message headers for
     * message that is being displayed.
     */
    private void displayMessageHeaders(MailUserData mud, Message msg,
            ServletOutputStream out)
            throws IOException {

        try {
            out.println("<b>Date:</b> " + msg.getSentDate() + "<br>");

            Address[] fr = msg.getFrom();
            if (fr != null) {
                boolean tf = true;
                out.print("<b>From:</b> ");
                for (int i = 0; i < fr.length; i++) {
                    out.print(((tf) ? " " : ", ") + getDisplayAddress(fr[i]));
                    tf = false;
                }
                out.println("<br>");
            }

            Address[] to = msg.getRecipients(Message.RecipientType.TO);
            if (to != null) {
                boolean tf = true;
                out.print("<b>To:</b> ");
                for (int i = 0; i < to.length; i++) {
                    out.print(((tf) ? " " : ", ") + getDisplayAddress(to[i]));
                    tf = false;
                }
                out.println("<br>");
            }

            Address[] cc = msg.getRecipients(Message.RecipientType.CC);
            if (cc != null) {
                boolean cf = true;
                out.print("<b>CC:</b> ");
                for (int i = 0; i < cc.length; i++) {
                    out.print(((cf) ? " " : ", ") + getDisplayAddress(cc[i]));
                    cf = false;
                }
                out.println("<br>");
            }

            out.print("<b>Subject:</b> "
                    + ((msg.getSubject() != null) ? msg.getSubject() : "")
                    + "<br>");

        } catch (MessagingException mex) {
            out.println(msg.toString());
        }
    }

    /**
     * This method displays the URL's for the available commands and the INBOX
     * headerlist
     */
    private void displayHeaders(MailUserData mud,
            HttpServletRequest req,
            ServletOutputStream out)
            throws IOException {

        SimpleDateFormat df = new SimpleDateFormat("EE M/d/yy");

        out.println("<html>");
        out.println("<HEAD><TITLE>JavaMail Servlet</TITLE></HEAD>");
        out.println("<BODY bgcolor=\"#ccccff\"><hr>");
        out.print("<center><font face=\"Arial,Helvetica\" font size=\"+3\">");
        out.println("<b>Folder " + mud.getStore().getURLName()
                + "/INBOX</b></font></center><p>");

        // URL's for the commands that are available
        out.println("<font face=\"Arial,Helvetica\" font size=\"+3\"><b>");
        out.println("<a href=\""
                + HttpUtils.getRequestURL(req)
                + "?logout=true\">Logout</a>");
        out.println("<a href=\""
                + HttpUtils.getRequestURL(req)
                + "?compose=true\" target=\"compose\">Compose</a>");
        out.println("</b></font>");
        out.println("<hr>");

        // List headers in a table
        out.print("<table cellpadding=1 cellspacing=1 "); // table
        out.println("width=\"100%\" border=1>");          // settings

        // sender column header
        out.println("<tr><td width=\"25%\" bgcolor=\"ffffcc\">");
        out.println("<font face=\"Arial,Helvetica\" font size=\"+1\">");
        out.println("<b>Sender</b></font></td>");
        // date column header
        out.println("<td width=\"15%\" bgcolor=\"ffffcc\">");
        out.println("<font face=\"Arial,Helvetica\" font size=\"+1\">");
        out.println("<b>Date</b></font></td>");
        // subject column header
        out.println("<td bgcolor=\"ffffcc\">");
        out.println("<font face=\"Arial,Helvetica\" font size=\"+1\">");
        out.println("<b>Subject</b></font></td></tr>");

        try {
            Folder f = mud.getFolder();
            int msgCount = f.getMessageCount();
            Message m = null;
            // for each message, show its headers
            for (int i = 1; i <= msgCount; i++) {
                m = f.getMessage(i);
                // if message has the DELETED flag set, don't display it
                if (m.isSet(Flags.Flag.DELETED)) {
                    continue;
                }
                boolean flag = m.getFrom() != null && (m.getFrom().length > 0);

                // from 
                out.println("<tr valigh=middle>");
                out.print("<td width=\"25%\" bgcolor=\"ffffff\">");
                if (flag) {
                    out.println("<font face=\"Arial,Helvetica\">"
                            + m.getFrom()[0].toString()
                            + "</font></td>");
                }
                // date

                out.print("<td nowrap width=\"15%\" bgcolor=\"ffffff\">");
                out.println("<font face=\"Arial,Helvetica\">"
                        + ((m.getSentDate() != null)
                        ? m.getSentDate() : m.getReceivedDate())
                        + "</font></td>");

                // subject & link
                out.print("<td bgcolor=\"ffffff\">");
                out.println("<font face=\"Arial,Helvetica\">"
                        + "<a href=\""
                        //                        + HttpUtils.getRequestURL(req)
                        + "?message="
                        + i + "\">"
                        //                        + ((m.getSubject() != null)
                        //                        ? m.getSubject()
                        //                        : "<i>No Subject</i>")
                        + "</a>"
                        + "</font></td>");
                out.println("</tr>");
            }
        } catch (MessagingException mex) {
            out.println("<tr><td>" + mex.toString() + "</td></tr>");
            mex.printStackTrace();
        }

        out.println("</table>");
        out.println("</BODY></html>");
        out.flush();
        out.close();
    }

    /**
     * This method handles the request when the user hits the <i>Compose</i>
     * link. It send the compose form to the browser.
     */
    private void compose(MailUserData mud, HttpServletResponse res,
            ServletOutputStream out)
            throws IOException {

        res.setContentType("text/html");
        out.println(composeForm);
        out.close();
    }

    /**
     * This method processes the send request from the compose form
     */
    private void send(HttpServletRequest req, HttpServletResponse res,
            ServletOutputStream out, HttpSession ssn)
            throws IOException {

        String to = req.getParameter("to");
        String cc = req.getParameter("cc");
        String subj = req.getParameter("subject");
        String text = req.getParameter("text");

        try {
            MailUserData mud = getMUD(ssn);
            if (mud == null) {
                throw new Exception("trying to send, but not logged in");
            }

            Message msg = new MimeMessage(mud.getSession());
            InternetAddress[] toAddrs = null, ccAddrs = null;
            if (to != null) {
                toAddrs = InternetAddress.parse(to, false);
                msg.setRecipients(Message.RecipientType.TO, toAddrs);
            } else {
                throw new MessagingException("No \"To\" address specified");
            }

            if (cc != null) {
                ccAddrs = InternetAddress.parse(cc, false);
                msg.setRecipients(Message.RecipientType.CC, ccAddrs);
            }

            if (subj != null) {
                msg.setSubject(subj);
            }

            URLName u = mud.getURLName();
            msg.setFrom(new InternetAddress(u.getUsername() + "@"
                    + u.getHost()));

            if (text != null) {
                msg.setText(text);
            }

            Transport.send(msg);

            out.println("<h1>Message sent successfully</h1></body></html>");
            out.close();

        } catch (Exception mex) {
            out.println("<h1>Error sending message.</h1>");
            out.println(mex.toString());
            out.println("<br></body></html>");
        }
    }

    // utility method; returns a string suitable for msg header display
    private String getDisplayAddress(Address a) {
        String pers = null;
        String addr = null;
        if (a instanceof InternetAddress
                && ((pers = ((InternetAddress) a).getPersonal()) != null)) {

            addr = pers + "  " + "&lt;" + ((InternetAddress) a).getAddress() + "&gt;";
        } else {
            addr = a.toString();
        }

        return addr;
    }

    // utility method; retrieve the MailUserData 
    // from the HttpSession and return it
    private MailUserData getMUD(HttpSession ses) throws IOException {
        MailUserData mud = null;

        if (ses == null) {
            return null;
        } else {
            if ((mud = (MailUserData) ses.getValue("javamailservlet")) == null) {
                return null;
            }
        }
        return mud;
    }

    public String getServletInfo() {
        return "A mail reader servlet";
    }
    /**
     * This is the HTML code for the compose form. Another option would have
     * been to use a separate html page.
     */
    private static String composeForm = "<HTML><HEAD><TITLE>JavaMail Compose</TITLE></HEAD><BODY BGCOLOR=\"#CCCCFF\"><FORM ACTION=\"JavaMailServlet\" METHOD=\"POST\"><input type=\"hidden\" name=\"send\" value=\"send\"><P ALIGN=\"CENTER\"><B><FONT SIZE=\"4\" FACE=\"Verdana, Arial, Helvetica\">JavaMail Compose Message</FONT></B><P><TABLE BORDER=\"0\" WIDTH=\"100%\"><TR><TD WIDTH=\"16%\" HEIGHT=\"22\">	<P ALIGN=\"RIGHT\"><B><FONT FACE=\"Verdana, Arial, Helvetica\">To:</FONT></B></TD><TD WIDTH=\"84%\" HEIGHT=\"22\"><INPUT TYPE=\"TEXT\" NAME=\"to\" SIZE=\"30\"> <FONT SIZE=\"1\" FACE=\"Verdana, Arial, Helvetica\"> (separate addresses with commas)</FONT></TD></TR><TR><TD WIDTH=\"16%\"><P ALIGN=\"RIGHT\"><B><FONT FACE=\"Verdana, Arial, Helvetica\">CC:</FONT></B></TD><TD WIDTH=\"84%\"><INPUT TYPE=\"TEXT\" NAME=\"cc\" SIZE=\"30\"> <FONT SIZE=\"1\" FACE=\"Verdana, Arial, Helvetica\"> (separate addresses with commas)</FONT></TD></TR><TR><TD WIDTH=\"16%\"><P ALIGN=\"RIGHT\"><B><FONT FACE=\"Verdana, Arial, Helvetica\">Subject:</FONT></B></TD><TD WIDTH=\"84%\"><INPUT TYPE=\"TEXT\" NAME=\"subject\" SIZE=\"55\"></TD></TR><TR><TD WIDTH=\"16%\">&nbsp;</TD><TD WIDTH=\"84%\"><TEXTAREA NAME=\"text\" ROWS=\"15\" COLS=\"53\"></TEXTAREA></TD></TR><TR><TD WIDTH=\"16%\" HEIGHT=\"32\">&nbsp;</TD><TD WIDTH=\"84%\" HEIGHT=\"32\"><INPUT TYPE=\"SUBMIT\" NAME=\"Send\" VALUE=\"Send\"><INPUT TYPE=\"RESET\" NAME=\"Reset\" VALUE=\"Reset\"></TD></TR></TABLE></FORM></BODY></HTML>";
}

/**
 * This class is used to store session data for each user's session. It is
 * stored in the HttpSession.
 */
class MailUserData {

    URLName url;
    Session session;
    Store store;
    Folder folder;

    public MailUserData(URLName urlname) {
        url = urlname;
    }

    public URLName getURLName() {
        return url;
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session s) {
        session = s;
    }

    public Store getStore() {
        return store;
    }

    public void setStore(Store s) {
        store = s;
    }

    public Folder getFolder() {
        return folder;
    }

    public void setFolder(Folder f) {
        folder = f;
    }
}
